package com.atguigu.daijia.payment.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.daijia.common.constant.MqConst;
import com.atguigu.daijia.common.execption.GuiguException;
import com.atguigu.daijia.common.result.ResultCodeEnum;
import com.atguigu.daijia.common.service.RabbitService;
import com.atguigu.daijia.driver.client.DriverAccountFeignClient;
import com.atguigu.daijia.model.entity.payment.PaymentInfo;
import com.atguigu.daijia.model.enums.TradeType;
import com.atguigu.daijia.model.form.driver.TransferForm;
import com.atguigu.daijia.model.form.payment.PaymentInfoForm;
import com.atguigu.daijia.model.vo.order.OrderRewardVo;
import com.atguigu.daijia.model.vo.payment.WxPrepayVo;
import com.atguigu.daijia.order.client.OrderInfoFeignClient;
import com.atguigu.daijia.payment.config.WxPayV3Properties;
import com.atguigu.daijia.payment.mapper.PaymentInfoMapper;
import com.atguigu.daijia.payment.service.WxPayService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import io.seata.spring.annotation.GlobalTransactional;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.Date;

@Service
@Slf4j
@RequiredArgsConstructor
public class WxPayServiceImpl implements WxPayService {

    private final PaymentInfoMapper paymentInfoMapper;
    private final RSAAutoCertificateConfig rsaAutoCertificateConfig;
    private final WxPayV3Properties wxPayV3Properties;
    private final RabbitService rabbitService;
    private final OrderInfoFeignClient orderInfoFeignClient;
    private final DriverAccountFeignClient driverAccountFeignClient;

    /**
     * 调用微信 JSAPI 下单接口，获取预支付交易会话标识
     *
     * @param paymentInfoForm
     * @return
     */
    @Override
    public WxPrepayVo createWxPayment(PaymentInfoForm paymentInfoForm) {
        try {
            // 查询当前订单的支付信息
            PaymentInfo paymentInfo = paymentInfoMapper.selectOne(new LambdaQueryWrapper<PaymentInfo>().eq(PaymentInfo::getOrderNo, paymentInfoForm.getOrderNo()));
            if (paymentInfo == null) {
                // 当前订单不存在支付信息，新增支付信息
                paymentInfo = new PaymentInfo();
                BeanUtils.copyProperties(paymentInfoForm, paymentInfo);
                paymentInfo.setPaymentStatus(0);
                paymentInfoMapper.insert(paymentInfo);
            }

            // 创建微信 JSAPI 支付服务实例
            JsapiServiceExtension jsapiService = new JsapiServiceExtension.Builder().config(rsaAutoCertificateConfig).build();

            // 构建预支付请求
            PrepayRequest prepayRequest = new PrepayRequest();
            // 订单金额信息，必填，object
            Amount amount = new Amount();
            // 订单总金额，单位为分，必填，integer
            // amount.setTotal(paymentInfo.getAmount().multiply(new BigDecimal(100)).intValue());
            amount.setTotal(1);// 测试用，实际开发中，金额需要从数据库中查询
            prepayRequest.setAmount(amount);
            // 小程序的 appid，必填，string(32)
            prepayRequest.setAppid(wxPayV3Properties.getAppId());
            // 直连商户号，必填，string(32)
            prepayRequest.setMchid(wxPayV3Properties.getMerchantId());
            // 商品描述，必填，string(127)
            String content = paymentInfo.getContent();
            if (content.length() > 127) {
                content = content.substring(0, 127);
            }
            prepayRequest.setDescription(content);
            // 异步接收微信支付结果通知的回调地址，必填，string(255)
            // 通知URL必须为外网可访问的URL，不能携带参数。 公网域名必须为HTTPS，如果是走专线接入，使用专线NAT IP或者私有回调域名可使用HTTP
            prepayRequest.setNotifyUrl(wxPayV3Properties.getNotifyUrl());
            // 支付者信息，必填，object
            Payer payer = new Payer();
            // 支付者的 openid，必填，string[1,128]
            payer.setOpenid(paymentInfo.getCustomerOpenId());
            // 结算信息，选填，object
            SettleInfo settleInfo = new SettleInfo();
            // true：指定分账，false：不指定分账
            settleInfo.setProfitSharing(true);
            prepayRequest.setSettleInfo(settleInfo);

            // 调用微信支付的 JSAPI 下单接口在微信支付服务后台生成预支付交易单，并且返回给商家预支付交易会话标识，用于后续接口调用中使用，该值有效期为2小时
            PrepayWithRequestPaymentResponse response = jsapiService.prepayWithRequestPayment(prepayRequest);
            log.info("微信支付的预支付交易会话标识：{}", response.getPackageVal());

            WxPrepayVo wxPrepayVo = new WxPrepayVo();
            BeanUtils.copyProperties(response, wxPrepayVo);
            wxPrepayVo.setTimeStamp(response.getTimeStamp());
            return wxPrepayVo;
        } catch (Exception e) {
            e.printStackTrace();
            throw new GuiguException(ResultCodeEnum.WX_JSAPI_PREPAY_ERROR);
        }
    }

    /**
     * 查询订单支付状态
     *
     * @param orderNo
     * @return
     */
    @Override
    public Boolean queryPayStatus(String orderNo) {
        // 构建查询订单支付状态的 Service 实例
        JsapiServiceExtension jsapiService = new JsapiServiceExtension.Builder().config(rsaAutoCertificateConfig).build();

        // 构建查询订单支付状态的请求
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setOutTradeNo(orderNo);
        request.setMchid(wxPayV3Properties.getMerchantId());

        Transaction transaction = jsapiService.queryOrderByOutTradeNo(request);
        if (transaction != null && Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())) {
            // 支付成功，执行后续业务逻辑
            this.handlePayment(transaction);
        } else {
            // 查询订单支付状态失败，抛出异常
            throw new GuiguException(ResultCodeEnum.WX_PAY_STATUS_FAIL);
        }
        return true;
    }

    /**
     * 微信支付结果异步通知
     *
     * @param request
     */
    @Override
    public void wxNotify(HttpServletRequest request) {
        // 1 回调通知的验签和解密
        // 从 request 头中获取参数
        // HTTP 头 Wechatpay-Serial
        String wechatPaySerial = request.getHeader("Wechatpay-Serial");
        // HTTP 头 Wechatpay-Nonce
        String nonce = request.getHeader("Wechatpay-Nonce");
        // HTTP 头 Wechatpay-Timestamp
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        // HTTP 头 Wechatpay-Signature
        String signature = request.getHeader("Wechatpay-Signature");
        // HTTP 头 Wechatpay-Signature-Type
        String signatureType = request.getHeader("Wechatpay-Signature-Type");

        // 获取请求体。切记使用原始报文，不要用 JSON 对象序列化后的字符串，避免验签的 body 和原文不一致
        StringBuilder sb = new StringBuilder();
        try {
            BufferedReader reader = request.getReader();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            throw new GuiguException(ResultCodeEnum.DATA_ERROR);
        }
        String requestBody = sb.toString();

        // 2 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(wechatPaySerial)
                .nonce(nonce)
                .signType(signatureType)
                .signature(signature)
                .timestamp(timestamp)
                .body(requestBody)
                .build();

        // 3 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);

        // 4 以支付通知回调为例，验签、解密并转换成 Transaction
        Transaction transaction = parser.parse(requestParam, Transaction.class);
        if (null != transaction && transaction.getTradeState() == Transaction.TradeStateEnum.SUCCESS) {
            // 5 处理支付业务
            this.handlePayment(transaction);
        }
    }

    /**
     * 支付成功，处理后续业务
     * @param transaction
     */
    private void handlePayment(Transaction transaction) {
        // 更新支付记录状态为已支付
        String orderNo = transaction.getOutTradeNo();
        PaymentInfo paymentInfo = paymentInfoMapper.selectOne(new LambdaQueryWrapper<PaymentInfo>().eq(PaymentInfo::getOrderNo, orderNo));
        if (1 == paymentInfo.getPaymentStatus()) {
            // 支付记录已经改为已支付，无需处理
            return;
        }
        paymentInfo.setPaymentStatus(1);
        paymentInfo.setOrderNo(orderNo);
        paymentInfo.setTransactionId(transaction.getTransactionId());
        paymentInfo.setCallbackTime(new Date());
        paymentInfo.setCallbackContent(JSON.toJSONString(transaction));
        paymentInfoMapper.updateById(paymentInfo);

        // 发送消息到到消息队列中，消息体为订单编号 orderNo，消费者端消费消息后处理后续业务逻辑
        rabbitService.sendMessage(MqConst.EXCHANGE_ORDER, MqConst.ROUTING_PAY_SUCCESS, orderNo);
    }

    @Override
    @GlobalTransactional
    public void handleOrder(String orderNo) {
        // 更新订单状态为已支付
        orderInfoFeignClient.updateOrderPayStatus(orderNo);

        // 获取订单的系统奖励，并转入司机账户
        OrderRewardVo orderRewardVo = orderInfoFeignClient.getOrderRewardFee(orderNo).getData();
        if (orderRewardVo != null && orderRewardVo.getRewardFee().doubleValue() > 0) {
            TransferForm transferForm = new TransferForm();
            transferForm.setDriverId(orderRewardVo.getDriverId());
            transferForm.setContent(TradeType.REWARD.getContent());
            transferForm.setTradeType(TradeType.REWARD.getType());
            transferForm.setAmount(orderRewardVo.getRewardFee());
            transferForm.setTradeNo(orderNo);
            driverAccountFeignClient.transfer(transferForm);
        }

        // TODO 分账
    }
}
